{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QdsgF9Mxj0gr"
      },
      "source": [
        "##### Adapted from the original [*A Swift Tour*](https://docs.swift.org/swift-book/GuidedTour/GuidedTour.html) on [Swift.org](https://swift.org) with modifications. The original content was authored by Apple Inc. Licensed under the [Creative Commons Attribution 4.0 International (CC BY 4.0) License](https://creativecommons.org/licenses/by/4.0/)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pJNp_GAv9LAh"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/swift/tutorials/a_swift_tour\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />View on TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ko/swift/tutorials/a_swift_tour.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ko/swift/tutorials/a_swift_tour.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "673eaebJdE12"
      },
      "source": [
        "# Swift 둘러보기\n",
        "\n",
        "새로운 언어로 작성된 첫 번째 프로그램에서는 화면에 'Hello, world!'라는 단어를 출력해야 하는 것이 전통적입니다. Swift에서는 한 줄로 이 작업을 수행할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "iNHY9-pzdE13"
      },
      "outputs": [],
      "source": [
        "print(\"Hello, world!\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jdHsgXTYdE17"
      },
      "source": [
        "C 또는 Objective-C로 코드를 작성했다면 이 구문은 익숙해 보입니다. Swift에서 이 코드는 완전한 프로그램입니다. 입력/출력 또는 문자열 처리와 같은 기능을 위해 별도의 라이브러리를 가져올 필요가 없습니다. 전역 범위에서 작성된 코드는 프로그램의 진입점으로 사용되므로 `main()` 함수가 필요하지 않습니다. 또한 모든 문 끝에 세미콜론을 쓸 필요가 없습니다.\n",
        "\n",
        "이 둘러보기는 다양한 프로그래밍 작업을 수행하는 방법을 보여줌으로써 Swift에서 코드 작성을 시작할 수 있는 충분한 정보를 제공합니다. 이해가 안 되는 부분이 있어도 걱정하지 마세요. 둘러보기에서 소개된 모든 내용은 이 책의 나머지 부분에서 자세히 설명되어 있습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OihtZA6QhEZS"
      },
      "source": [
        "## 단순 값\n",
        "\n",
        "`let`을 사용하여 상수를 만들고 `var`을 사용하여 변수를 만듭니다. 상수값을 컴파일할 때 알 필요는 없지만 이 값은 정확히 한 번만 할당되어야 합니다. 즉, 상수를 사용하여 한 번 결정하지만 여러 곳에서 사용하는 값의 이름을 지정할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "p_pjyevTdE18"
      },
      "outputs": [],
      "source": [
        "var myVariable = 42\n",
        "myVariable = 50\n",
        "let myConstant = 42"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "A7S47TpWdE1-"
      },
      "source": [
        "상수 또는 변수는 할당하려는 값과 같은 유형이어야 합니다. 그러나 항상 유형을 명시적으로 작성할 필요는 없습니다. 상수 또는 변수를 만들 때 값을 제공하면 컴파일러가 해당 유형을 추론할 수 있습니다. 위의 예에서 컴파일러는 초기값이 정수이므로 `myVariable`이 정수라고 추론합니다.\n",
        "\n",
        "초기값이 충분한 정보를 제공하지 않거나 초기값이 없는 경우 콜론으로 구분하고 변수 뒤에 작성하여 유형을 지정하세요. 참고: 부동 소수점 숫자에 `Float` 대신 `Double`을 사용하면 더 높은 정밀도를 얻을 수 있으며 이는 Swift의 기본 부동 소수점 유형입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "RDo7IwtmdE1_"
      },
      "outputs": [],
      "source": [
        "let implicitInteger = 70\n",
        "let implicitDouble = 70.0\n",
        "let explicitDouble: Double = 70"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9jZ0Hccqhlil"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Create a constant with an explicit type of `Float` and a value of 4."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "N1PqnuLFXR4L"
      },
      "source": [
        "값은 암시적으로 다른 유형으로 변환되지 않습니다. 값을 다른 유형으로 변환하려면 원하는 유형의 인스턴스를 명시적으로 만듭니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rPuJLYsHdE2C"
      },
      "outputs": [],
      "source": [
        "let label = \"The width is \"\n",
        "let width = 94\n",
        "label + String(width)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QuQ7-JoOhNY9"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Try removing the conversion to `String` from the last line. What error do you get?"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7cwpu6_3YYKX"
      },
      "source": [
        "문자열에 값을 포함하는 더 간단한 방법이 있습니다. 괄호 안에 값을 작성하고 괄호 앞에 백 슬래시(`문자열에 값을 포함하는 더 간단한 방법이 있습니다. 괄호 안에 값을 작성하고 괄호 앞에 백 슬래시()를 작성합니다. 예제는 다음과 같습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "j-W3HyT9ZBOF"
      },
      "outputs": [],
      "source": [
        "let apples = 3\n",
        "\"I have \\(apples) apples.\""
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kd1WEYjwZCXx"
      },
      "outputs": [],
      "source": [
        "let oranges = 5\n",
        "\"I have \\(apples + oranges) pieces of fruit.\""
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3avZRAzlguOQ"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Use `\\()` to include a floating-point calculation in a string and to include someone's name in a\n",
        "// greeting."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4T6rPJ6tWaKu"
      },
      "source": [
        "여러 줄을 차지하는 문자열에는 세 개의 큰 따옴표(`\"\"\"`)를 사용합니다. 닫는 따옴표의 들여쓰기와 일치하는 한 따옴표로 묶인 각 줄의 시작 부분에 있는 들여쓰기는 제거됩니다. 예제는 다음과 같습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "_5RJZBV1Webt"
      },
      "outputs": [],
      "source": [
        "let quotation = \"\"\"\n",
        "    Even though there's whitespace to the left,\n",
        "    the actual lines aren't indented.\n",
        "        Except for this line.\n",
        "    Double quotes (\") can appear without being escaped.\n",
        "\n",
        "    I still have \\(apples + oranges) pieces of fruit.\n",
        "    \"\"\"\n",
        "print(quotation)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AuhL5LMzVr-T"
      },
      "source": [
        "대괄호(`[]`)를 사용하여 배열과 사전을 만들고 대괄호 안에 인덱스 또는 키를 작성하여 해당 요소에 액세스합니다. 마지막 요소 뒤에 쉼표가 허용됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "BI46PGfQdE2I"
      },
      "outputs": [],
      "source": [
        "var shoppingList = [\"catfish\", \"water\", \"tulips\", \"blue paint\"]\n",
        "shoppingList[1] = \"bottle of water\"\n",
        " \n",
        "var occupations = [\n",
        "    \"Malcolm\": \"Captain\",\n",
        "    \"Kaylee\": \"Mechanic\",\n",
        "]\n",
        "occupations[\"Jayne\"] = \"Public Relations\"\n",
        "occupations"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HpaZFmGhYmW-"
      },
      "source": [
        "요소를 추가하면 배열이 자동으로 커집니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vVgbHKpaYpPX"
      },
      "outputs": [],
      "source": [
        "shoppingList.append(\"blue paint\")\n",
        "shoppingList"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "P_F6ODC6dE2L"
      },
      "source": [
        "빈 배열 또는 사전을 만들려면 이니셜라이저 구문을 사용합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "pZ0hFNzXdE2M"
      },
      "outputs": [],
      "source": [
        "let emptyArray = [String]()\n",
        "let emptyDictionary = [String: Float]()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "r88SNR3mdE2Q"
      },
      "source": [
        "유형 정보를 추론할 수 있는 경우 빈 배열을 `[]`로 하고 빈 사전을 `[:]`로 작성할 수 있습니다. 예를 들어, 변수에 대한 새 값을 설정하거나 함수에 인수를 전달할 때입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LrmOoh8HdE2R"
      },
      "outputs": [],
      "source": [
        "shoppingList = []\n",
        "occupations = [:]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IcyJIWOqdE2T"
      },
      "source": [
        "## 제어 흐름\n",
        "\n",
        "조건문을 만들려면 `if` 및 `switch`를 사용하고, 루프를 만들려면 `for`-`in`, `for` , `while` 및 `repeat`-`while` 을 사용합니다. 조건 또는 루프 변수를 감싸는 괄호는 선택 사항입니다. 본문은 중괄호로 감쌉니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QhcmCbQydE2U"
      },
      "outputs": [],
      "source": [
        "let individualScores = [75, 43, 103, 87, 12]\n",
        "var teamScore = 0\n",
        "for score in individualScores {\n",
        "    if score > 50 {\n",
        "        teamScore += 3\n",
        "    } else {\n",
        "        teamScore += 1\n",
        "    }\n",
        "}\n",
        "teamScore"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VfEX-a-JdE2W"
      },
      "source": [
        "`if` 문에서 조건은 부울 식이어야 합니다. 즉, `if score { ... }`와 같은 코드는 0에 대한 암시적 비교가 아니라 오류입니다.\n",
        "\n",
        "`if`와 `let`을 함께 사용하여 누락될 수 있는 값을 처리할 수 있습니다. 이러한 값은 선택 사항으로 표시됩니다. 선택적 값은 값을 포함하거나 값이 누락되었음을 나타내는 `nil`을 포함합니다. 값을 선택 사항으로 표시하려면 값 유형 뒤에 물음표 (`?`)를 작성하세요."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "fCqYihmIdE2X"
      },
      "outputs": [],
      "source": [
        "var optionalString: String? = \"Hello\"\n",
        "optionalString == nil"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3GcY1w13hv82"
      },
      "outputs": [],
      "source": [
        "var optionalName: String? = \"John Appleseed\"\n",
        "var greeting = \"Hello!\"\n",
        "if let name = optionalName {\n",
        "    greeting = \"Hello, \\(name)\"\n",
        "}\n",
        "greeting"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "0dJr-IC8giJL"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Change `optionalName` to `nil`. What greeting do you get?\n",
        "// Add an `else` clause that sets a different greeting if `optionalName` is `nil`."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZZWOeB3ve-jm"
      },
      "source": [
        "선택적 값이 `nil`이면 조건은 `false`이고 중괄호 안의 코드는 건너뜁니다. 그렇지 않으면 선택적 값은 래핑 해제되고 `let` 뒤의 상수에 할당되어 코드 블록 내에서 래핑 해제된 값을 사용할 수 있습니다.\n",
        "\n",
        "선택적 값을 처리하는 또 다른 방법은 `??` 연산자를 사용하여 기본값을 제공하는 것입니다. 선택적 값이 누락된 경우 대신 기본값이 사용됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "UqstABf4dE2b"
      },
      "outputs": [],
      "source": [
        "let nickName: String? = nil\n",
        "let fullName: String = \"John Appleseed\"\n",
        "\"Hi \\(nickName ?? fullName)\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "l_3enGZldE2d"
      },
      "source": [
        "switch는 모든 종류의 데이터와 다양한 비교 연산을 지원합니다. switch는 정수 및 등식 테스트로 제한되지 않습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "AXTHsCcedE2e"
      },
      "outputs": [],
      "source": [
        "let vegetable = \"red pepper\"\n",
        "switch vegetable {\n",
        "case \"celery\":\n",
        "    print(\"Add some raisins and make ants on a log.\")\n",
        "case \"cucumber\", \"watercress\":\n",
        "    print(\"That would make a good tea sandwich.\")\n",
        "case let x where x.hasSuffix(\"pepper\"):\n",
        "    print(\"Is it a spicy \\(x)?\")\n",
        "default:\n",
        "    print(\"Everything tastes good in soup.\")\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "e-FK9XcGga0O"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Try removing the default case. What error do you get?"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AXaHBPiidE2g"
      },
      "source": [
        "패턴의 해당 부분과 일치하는 값을 상수에 할당하기 위해 패턴에서 `let`을 사용하는 방법에 주목합니다.\n",
        "\n",
        "일치하는 switch case 내부의 코드를 실행한 후 프로그램은 switch 문을 종료합니다. 실행은 다음 case로 계속되지 않으므로 각 case의 코드 끝에서 switch를 명시적으로 중단할 필요가 없습니다.\n",
        "\n",
        "`for`-`in`을 사용하여 각 키-값 쌍에 사용할 이름 쌍을 제공하여 사전의 항목을 반복합니다. 사전은 정렬되지 않은 모음이므로 해당 키와 값은 임의의 순서로 반복됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nJHpFEDPdE2h"
      },
      "outputs": [],
      "source": [
        "let interestingNumbers = [\n",
        "    \"Prime\": [2, 3, 5, 7, 11, 13],\n",
        "    \"Fibonacci\": [1, 1, 2, 3, 5, 8],\n",
        "    \"Square\": [1, 4, 9, 16, 25],\n",
        "]\n",
        "var largest = 0\n",
        "for (kind, numbers) in interestingNumbers {\n",
        "    for number in numbers {\n",
        "        if number > largest {\n",
        "            largest = number\n",
        "        }\n",
        "    }\n",
        "}\n",
        "largest"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "UKKf8zkRgWUq"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Add another variable to keep track of which kind of number was the largest, as well as what that\n",
        "// largest number was."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "or2W6kbyfuXu"
      },
      "source": [
        "조건이 변경될 때까지 코드 블록을 반복하려면 `while`을 사용합니다. 대신 루프의 조건이 끝에 있을 수 있으므로 루프가 한 번 이상 실행되도록 합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tt54-NuJdE2m"
      },
      "outputs": [],
      "source": [
        "var n = 2\n",
        "while n < 100 {\n",
        "    n = n * 2\n",
        "}\n",
        "\n",
        "n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "adAijrWkiAGF"
      },
      "outputs": [],
      "source": [
        "var m = 2\n",
        "repeat {\n",
        "    m = m * 2\n",
        "} while m < 100\n",
        "\n",
        "m"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DqEff6OfdE2q"
      },
      "source": [
        "`..<`를 사용하여 인덱스 범위를 만들거나 명시적 초기화, 조건 및 증분을 작성하여 인덱스를 루프에 유지할 수 있습니다. 다음 두 루프는 같은 작업을 수행합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LKkJ4co_dE2r"
      },
      "outputs": [],
      "source": [
        "var total = 0\n",
        "for i in 0..<4 {\n",
        "    total += i\n",
        "}\n",
        "\n",
        "total"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HKgeaTprdE2t"
      },
      "source": [
        "`..<`를 사용하여 상위값을 생략하는 범위를 만들고 `...`를 사용하여 두 값을 모두 포함하는 범위를 만듭니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W7H3ptGaiEcB"
      },
      "source": [
        "## 함수와 클로저\n",
        "\n",
        "함수를 선언하려면 `func`를 사용하세요. 괄호로 묶인 인수 목록과 함께 이름을 따라 함수를 호출합니다. `->`을 사용하여 함수의 반환 유형에서 매개변수 이름과 유형을 구분합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6u_fX5SwdE2t"
      },
      "outputs": [],
      "source": [
        "func greet(name: String, day: String) -> String {\n",
        "    return \"Hello \\(name), today is \\(day).\"\n",
        "}\n",
        "greet(name: \"Bob\", day: \"Tuesday\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "sgYex6gigH87"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Remove the `day` parameter. Add a parameter to include today’s lunch special in the greeting."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Mo9D_hc0jfkF"
      },
      "source": [
        "기본적으로 함수는 매개변수 이름을 인수의 레이블로 사용합니다. 매개변수 이름 앞에 사용자 정의 인수 레이블을 작성하거나 인수 레이블을 사용하지 않으려면 `_`를 작성합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8Dkx71QUjisV"
      },
      "outputs": [],
      "source": [
        "func greet(_ person: String, on day: String) -> String {\n",
        "    return \"Hello \\(person), today is \\(day).\"\n",
        "}\n",
        "greet(\"John\", on: \"Wednesday\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "71qFe0AWjd9F"
      },
      "source": [
        "튜플을 사용하여 복합값을 만듭니다. 예를 들어, 함수에서 여러 값을 반환하는 데 사용합니다. 튜플의 요소는 이름이나 번호로 참조할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5cDR7-TydE2w"
      },
      "outputs": [],
      "source": [
        "func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {\n",
        "    var min = scores[0]\n",
        "    var max = scores[0]\n",
        "    var sum = 0\n",
        "    \n",
        "    for score in scores {\n",
        "        if score > max {\n",
        "            max = score\n",
        "        } else if score < min {\n",
        "            min = score\n",
        "        }\n",
        "        sum += score\n",
        "    }\n",
        "    \n",
        "    return (min, max, sum)\n",
        "}\n",
        "let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])\n",
        "print(statistics.sum)\n",
        "print(statistics.2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hHUWHaH-jwmx"
      },
      "source": [
        "함수는 중첩될 수 있습니다. 중첩 함수는 외부 함수에서 선언된 변수에 액세스할 수 있습니다. 중첩 함수를 사용하여 길거나 복잡한 함수에서 코드를 구성할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "K094099VdE21"
      },
      "outputs": [],
      "source": [
        "func returnFifteen() -> Int {\n",
        "    var y = 10\n",
        "    func add() {\n",
        "        y += 5\n",
        "    }\n",
        "    add()\n",
        "    return y\n",
        "}\n",
        "returnFifteen()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "s8hfkf4HdE24"
      },
      "source": [
        "함수는 일급 유형입니다. 이는 함수가 다른 함수를 값으로 반환할 수 있음을 의미합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "sV33PIdsdE26"
      },
      "outputs": [],
      "source": [
        "func makeIncrementer() -> ((Int) -> Int) {\n",
        "    func addOne(number: Int) -> Int {\n",
        "        return 1 + number\n",
        "    }\n",
        "    return addOne\n",
        "}\n",
        "var increment = makeIncrementer()\n",
        "increment(7)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "J6-9e3godE29"
      },
      "source": [
        "함수는 다른 함수를 인수 중 하나로 사용할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nOwBtGWLdE2-"
      },
      "outputs": [],
      "source": [
        "func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {\n",
        "    for item in list {\n",
        "        if condition(item) {\n",
        "            return true\n",
        "        }\n",
        "    }\n",
        "    return false\n",
        "}\n",
        "func lessThanTen(number: Int) -> Bool {\n",
        "    return number < 10\n",
        "}\n",
        "var numbers = [20, 19, 7, 12]\n",
        "hasAnyMatches(list: numbers, condition: lessThanTen)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "u9uVmoK6dE3B"
      },
      "source": [
        "함수는 실제로 클로저의 특별한 경우로 나중에 호출할 수 있는 코드 블록입니다. 클로저의 코드는 클로저가 실행될 때 다른 범위에 있더라도 클로저가 만들어진 범위에서 사용할 수 있는 변수, 함수 등에 액세스할 수 있습니다. 이미 중첩된 함수가 있는 예제를 보았습니다. 중괄호(`{}`)로 코드를 묶어 이름 없이 클로저를 작성할 수 있습니다. `in` 을 사용하여 인수를 분리하고 본문에서 유형을 반환합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qWTL5QMKdE3C"
      },
      "outputs": [],
      "source": [
        "numbers.map({ (number: Int) -> Int in\n",
        "    let result = 3 * number\n",
        "    return result\n",
        "})"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "aTZoZCybf2uG"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Rewrite the closure to return zero for all odd numbers."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NapuA70Qj8QF"
      },
      "source": [
        "클로저를 보다 간결하게 작성할 수 있는 몇 가지 옵션이 있습니다. 대리자에 대한 콜백과 같이 클로저의 유형이 이미 알려진 경우 매개변수 유형, 반환 유형 또는 둘 다를 생략할 수 있습니다. 단일 문 클로저는 암시적으로 유일한 문 값을 반환합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "wNcmSyFodE3F"
      },
      "outputs": [],
      "source": [
        "let mappedNumbers = numbers.map({ number in 3 * number })\n",
        "print(mappedNumbers)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6MxBFQjRdE3I"
      },
      "source": [
        "이름 대신 숫자로 매개변수를 참조할 수 있습니다. 이 접근 방식은 매우 짧은 클로저에 특히 유용합니다. 함수의 마지막 인수로 전달된 클로저는 괄호 바로 뒤에 나타날 수 있습니다. 클로저가 함수의 유일한 인수이면 괄호를 완전히 생략할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6Ecv1ethdE3I"
      },
      "outputs": [],
      "source": [
        "let sortedNumbers = numbers.sorted { $0 > $1 }\n",
        "print(sortedNumbers)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BfV1d5HpdE3K"
      },
      "source": [
        "## 객체와 클래스\n",
        "\n",
        "`class` 뒤에 클래스 이름을 사용하여 클래스를 만듭니다. 클래스에서 속성 선언은 클래스 컨텍스트라는 점을 제외하면 상수 또는 변수 선언과 같은 방식으로 작성됩니다. 마찬가지로 메서드와 함수 선언도 같은 방식으로 작성됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SycTCbxadE3L"
      },
      "outputs": [],
      "source": [
        "class Shape {\n",
        "    var numberOfSides = 0\n",
        "    func simpleDescription() -> String {\n",
        "        return \"A shape with \\(numberOfSides) sides.\"\n",
        "    }\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "i-oNcgg4h-wm"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Add a constant property with `let`, and add another method that takes an argument."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NDuIuzgUkAEY"
      },
      "source": [
        "클래스 이름 뒤에 괄호를 넣어 클래스의 인스턴스를 만듭니다. 점 구문을 사용하여 인스턴스의 속성 및 메서드에 액세스합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "y1_AYUbidE3O"
      },
      "outputs": [],
      "source": [
        "var shape = Shape()\n",
        "shape.numberOfSides = 7\n",
        "var shapeDescription = shape.simpleDescription()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "id0q1vIfdE3Q"
      },
      "source": [
        "이 버전의 `Shape` 클래스에는 인스턴스가 만들어질 때 클래스를 설정하는 이니셜라이저가 누락되었습니다. `init`를 사용하여 이니셜라이저를 만듭니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "lboFcMVtdE3Q"
      },
      "outputs": [],
      "source": [
        "class NamedShape {\n",
        "    var numberOfSides: Int = 0\n",
        "    var name: String\n",
        "    \n",
        "    init(name: String) {\n",
        "        self.name = name\n",
        "    }\n",
        "    \n",
        "    func simpleDescription() -> String {\n",
        "        return \"A shape with \\(numberOfSides) sides.\"\n",
        "    }\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "h06b-4fKdE3S"
      },
      "source": [
        "이니셜라이저에 대한 `name` 인수와 `name` 속성을 구별하기 위해 `self`가 어떻게 사용되는지에 주목합니다. 이니셜라이저에 대한 인수는 클래스의 인스턴스를 만들 때 함수 호출처럼 전달됩니다. 모든 속성에는 선언(`numberOfSides`) 또는 이니셜라이저(`name`)에 할당된 값이 필요합니다.\n",
        "\n",
        "객체가 할당 해제되기 전에 정리를 수행하려면 `deinit`를 사용하여 디이니셜라이저(deinitializer)를 만듭니다.\n",
        "\n",
        "서브 클래스는 클래스 이름 뒤에 슈퍼클래스 이름을 콜론으로 구분하여 포함합니다. 클래스에서 표준 루트 클래스를 서브 클래스화할 필요가 없으므로 필요에 따라 슈퍼클래스를 포함하거나 생략할 수 있습니다.\n",
        "\n",
        "슈퍼클래스의 구현을 재정의하는 서브 클래스의 메서드는 `override`로 표시됩니다. `override` 없이 실수로 메서드를 재정의하면 컴파일러에서 오류로 감지됩니다. 또한 컴파일러는 슈퍼클래스의 어떤 메서드도 실제로 재정의하지 않는 메서드를 `override`로 감지합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "VHT1Lpz8dE3S"
      },
      "outputs": [],
      "source": [
        "class Square: NamedShape {\n",
        "    var sideLength: Double\n",
        "    \n",
        "    init(sideLength: Double, name: String) {\n",
        "        self.sideLength = sideLength\n",
        "        super.init(name: name)\n",
        "        numberOfSides = 4\n",
        "    }\n",
        "    \n",
        "    func area() -> Double {\n",
        "        return sideLength * sideLength\n",
        "    }\n",
        "    \n",
        "    override func simpleDescription() -> String {\n",
        "        return \"A square with sides of length \\(sideLength).\"\n",
        "    }\n",
        "}\n",
        "let test = Square(sideLength: 5.2, name: \"my test square\")\n",
        "test.area()\n",
        "test.simpleDescription()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SIoEXii5iDA3"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// - Make another subclass of `NamedShape` called `Circle` that takes a radius and a name as\n",
        "//   arguments to its initializer.\n",
        "// - Implement an `area()` and a `simpleDescription()` method on the `Circle` class."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-jWyfN7KkDqR"
      },
      "source": [
        "저장된 간단한 속성 외에도 속성에는 getter 및 setter가 있을 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QaqZC56MdE3V"
      },
      "outputs": [],
      "source": [
        "class EquilateralTriangle: NamedShape {\n",
        "    var sideLength: Double = 0.0\n",
        "    \n",
        "    init(sideLength: Double, name: String) {\n",
        "        self.sideLength = sideLength\n",
        "        super.init(name: name)\n",
        "        numberOfSides = 3\n",
        "    }\n",
        "    \n",
        "    var perimeter: Double {\n",
        "        get {\n",
        "            return 3.0 * sideLength\n",
        "        }\n",
        "        set {\n",
        "            sideLength = newValue / 3.0\n",
        "        }\n",
        "    }\n",
        "    \n",
        "    override func simpleDescription() -> String {\n",
        "        return \"An equilateral triangle with sides of length \\(sideLength).\"\n",
        "    }\n",
        "}\n",
        "var triangle = EquilateralTriangle(sideLength: 3.1, name: \"a triangle\")\n",
        "print(triangle.perimeter)\n",
        "triangle.perimeter = 9.9\n",
        "print(triangle.sideLength)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CK3s64WhdE3W"
      },
      "source": [
        "`perimeter`에 대한 setter에서 새 값은 암시적 이름 `newValue`를 갖습니다. `set` 뒤에 괄호 안에 명시적인 이름을 제공할 수 있습니다.\n",
        "\n",
        "`EquilateralTriangle` 클래스의 이니셜라이저에는 3가지 단계가 있습니다.\n",
        "\n",
        "1. 서브 클래스가 선언하는 속성값 설정\n",
        "\n",
        "2. 슈퍼클래스의 이니셜라이저 호출\n",
        "\n",
        "3. 슈퍼클래스에서 정의한 속성값 변경(메서드, getter 또는 setter를 사용하는 추가 설정 작업도 이 시점에서 수행할 수 있습니다.)\n",
        "\n",
        "속성을 계산할 필요는 없지만 새 값을 설정하기 전과 후에 실행되는 코드를 제공하려면 `willSet` 및 `didSet`를 사용합니다. 제공된 코드는 이니셜라이저 외부에서 값이 변경될 때마다 실행됩니다. 예를 들어, 아래 클래스는 삼각형의 측면 길이가 항상 사각형의 측면 길이와 같다는 것을 확인합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "VSPCTz5mdE3a"
      },
      "outputs": [],
      "source": [
        "class TriangleAndSquare {\n",
        "    var triangle: EquilateralTriangle {\n",
        "        willSet {\n",
        "            square.sideLength = newValue.sideLength\n",
        "        }\n",
        "    }\n",
        "    var square: Square {\n",
        "        willSet {\n",
        "            triangle.sideLength = newValue.sideLength\n",
        "        }\n",
        "    }\n",
        "    init(size: Double, name: String) {\n",
        "        square = Square(sideLength: size, name: name)\n",
        "        triangle = EquilateralTriangle(sideLength: size, name: name)\n",
        "    }\n",
        "}\n",
        "var triangleAndSquare = TriangleAndSquare(size: 10, name: \"another test shape\")\n",
        "print(triangleAndSquare.square.sideLength)\n",
        "print(triangleAndSquare.triangle.sideLength)\n",
        "triangleAndSquare.square = Square(sideLength: 50, name: \"larger square\")\n",
        "print(triangleAndSquare.triangle.sideLength)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ygWAN55xdE3b"
      },
      "source": [
        "선택적 값을 처리할 때는 메서드, 속성 및 아래 첨자와 같은 연산 전에 `?`를 작성할 수 있습니다. `?` 전에 값이 `nil`인 경우, `?` 뒤에 모든 것은 무시되고 전체 표현식의 값은 `nil`입니다. 그렇지 않으면 선택적 값이 래핑 해제되고 `?` 뒤에 모든 것은 래핑 해제된 값에 대해 동작합니다. 두 경우 모두 전체 표현식의 값은 선택적 값입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "pmXsLt_gdE3c"
      },
      "outputs": [],
      "source": [
        "let optionalSquare: Square? = Square(sideLength: 2.5, name: \"optional square\")\n",
        "optionalSquare?.sideLength"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0yaZFHMRdE3d"
      },
      "source": [
        "## 열거형과 구조\n",
        "\n",
        "`enum`을 사용하여 열거형을 만듭니다. 클래스 및 다른 모든 명명된 형식과 마찬가지로 열거형에는 연결된 메서드가 있을 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Btg1_6rXdE3e"
      },
      "outputs": [],
      "source": [
        "enum Rank: Int {\n",
        "    case ace = 1\n",
        "    case two, three, four, five, six, seven, eight, nine, ten\n",
        "    case jack, queen, king\n",
        "\n",
        "    func simpleDescription() -> String {\n",
        "        switch self {\n",
        "        case .ace:\n",
        "            return \"ace\"\n",
        "        case .jack:\n",
        "            return \"jack\"\n",
        "        case .queen:\n",
        "            return \"queen\"\n",
        "        case .king:\n",
        "            return \"king\"\n",
        "        default:\n",
        "            return String(self.rawValue)\n",
        "        }\n",
        "    }\n",
        "}\n",
        "let ace = Rank.ace\n",
        "print(ace)\n",
        "let aceRawValue = ace.rawValue\n",
        "print(aceRawValue)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "34Ba8HGwin9n"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Write a function that compares two `Rank` values by comparing their raw values."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0R1w6q3KkIH1"
      },
      "source": [
        "기본적으로 Swift는 0에서 시작하여 매번 증가하는 원시값을 할당하지만, 명시적으로 값을 지정하여 이 동작을 변경할 수 있습니다. 위의 예에서 Ace에는 명시적으로 원시값 `1`이 지정되고 나머지 원시값은 순서대로 지정됩니다. 또한 문자열 또는 부동 소수점 숫자를 원시 유형의 열거형으로 사용할 수 있습니다. 열거형 case의 원시값에 액세스하려면 `rawValue` 속성을 사용합니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zgtd86KUp5qa"
      },
      "source": [
        "`init?(rawValue:)` 이니셜라이저를 사용하여 원시값에서 열거형 인스턴스를 만듭니다. 원시값과 일치하는 열거형 case를 반환하거나, 일치하는 `Rank`가 없으면 `nil`을 반환합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "BjzxKC67dE3h"
      },
      "outputs": [],
      "source": [
        "if let convertedRank = Rank(rawValue: 3) {\n",
        "    let threeDescription = convertedRank.simpleDescription()\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aViNXJOWdE3k"
      },
      "source": [
        "열거형의 case 값은 원시값을 작성하는 다른 방법이 아니라 실제값입니다. 실제로 의미 있는 원시값이 없다면 원시값을 제공할 필요가 없습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "i9fKzaQzdE3k"
      },
      "outputs": [],
      "source": [
        "enum Suit {\n",
        "    case spades, hearts, diamonds, clubs\n",
        "\n",
        "    func simpleDescription() -> String {\n",
        "        switch self {\n",
        "        case .spades:\n",
        "            return \"spades\"\n",
        "        case .hearts:\n",
        "            return \"hearts\"\n",
        "        case .diamonds:\n",
        "            return \"diamonds\"\n",
        "        case .clubs:\n",
        "            return \"clubs\"\n",
        "        }\n",
        "    }\n",
        "}\n",
        "let hearts = Suit.hearts\n",
        "let heartsDescription = hearts.simpleDescription()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "H-38gQ27isHk"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Add a `color()` method to `Suit` that returns \"black\" for spades and clubs, and returns \"red\" for\n",
        "// hearts and diamonds."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "H1B0m_prdE3m"
      },
      "source": [
        "열거형의 `Hearts` case가 위에서 언급되는 두 가지 방법에 주목합니다. `hearts` 상수에 값을 할당할 때 상수에 명시적 유형이 지정되지 않았으므로 열거형 case `Suit.Hearts`는 전체 이름으로 참조됩니다. switch 내에서 열거형 case는 `self`의 값이 이미 suit로 알려져서 축약형 `.Hearts`로 참조됩니다. 값의 유형이 이미 알려지면 언제든지 축약형을 사용할 수 있습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fCDHUHBHdE3o"
      },
      "source": [
        "열거형에 원시값이 있는 경우 해당 값은 선언의 일부로 결정됩니다. 즉, 특정 열거형 case의 모든 인스턴스는 항상 같은 원시값을 갖습니다. 열거형 case에 대한 또 다른 선택은 case와 연결된 값을 갖는 것입니다. 이들 값은 인스턴스를 만들 때 결정되며 열거형 case의 인스턴스마다 다를 수 있습니다. 연결된 값은 열거형 case 인스턴스의 저장된 속성처럼 동작한다고 생각하면 됩니다.\n",
        "\n",
        "서버에서 일출 및 일몰 시각을 요청하는 경우를 예로 들 수 있습니다. 서버는 요청된 정보로 응답하거나 무엇이 잘못되었는지에 대한 설명으로 응답합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "v7CDdsdydE3o"
      },
      "outputs": [],
      "source": [
        "enum ServerResponse {\n",
        "    case result(String, String)\n",
        "    case failure(String)\n",
        "}\n",
        "\n",
        "let success = ServerResponse.result(\"6:00 am\", \"8:09 pm\")\n",
        "let failure = ServerResponse.failure(\"Out of cheese.\")\n",
        "\n",
        "switch success {\n",
        "case let .result(sunrise, sunset):\n",
        "    print(\"Sunrise is at \\(sunrise) and sunset is at \\(sunset).\")\n",
        "case let .failure(message):\n",
        "    print(\"Failure...  \\(message)\")\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6KxPZi0ii4Jj"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Add a third case to `ServerResponse` and to the switch."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BCjUxgx0iKDh"
      },
      "source": [
        "switch case에서 일치하는 값의 일부로 `ServerResponse` 값에서 일출 및 일몰 시각이 어떻게 추출되는지 확인합니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FonxodFmpPXv"
      },
      "source": [
        "`struct`를 사용하여 구조를 만듭니다. 구조체는 메서드와 이니셜라이저를 포함하여 클래스와 같은 동작을 많이 지원합니다. 구조와 클래스의 가장 중요한 차이점 중 하나는 구조가 코드에서 전달될 때 항상 복사되지만, 클래스는 참조로 전달된다는 것입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tsTnhpukpTF1"
      },
      "outputs": [],
      "source": [
        "struct Card {\n",
        "    var rank: Rank\n",
        "    var suit: Suit\n",
        "    func simpleDescription() -> String {\n",
        "        return \"The \\(rank.simpleDescription()) of \\(suit.simpleDescription())\"\n",
        "    }\n",
        "}\n",
        "let threeOfSpades = Card(rank: .three, suit: .spades)\n",
        "let threeOfSpadesDescription = threeOfSpades.simpleDescription()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FM1U4SqwpUp6"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Write a function that returns an array containing a full deck of cards, with one card of each\n",
        "// combination of rank and suit."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jUBjQqUhdE3p"
      },
      "source": [
        "## 프로토콜과 확장\n",
        "\n",
        "프로토콜을 선언하려면 `protocol`을 사용합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vVEIH7WhdE3q"
      },
      "outputs": [],
      "source": [
        "protocol ExampleProtocol {\n",
        "    var simpleDescription: String { get }\n",
        "    mutating func adjust()\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GSV2yv6SdE3r"
      },
      "source": [
        "클래스, 열거형 및 구조체는 모두 프로토콜을 채택할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Lgf8If-0dE3r"
      },
      "outputs": [],
      "source": [
        "class SimpleClass: ExampleProtocol {\n",
        "    var simpleDescription: String = \"A very simple class.\"\n",
        "    var anotherProperty: Int = 69105\n",
        "    func adjust() {\n",
        "        simpleDescription += \"  Now 100% adjusted.\"\n",
        "    }\n",
        "}\n",
        "var a = SimpleClass()\n",
        "a.adjust()\n",
        "let aDescription = a.simpleDescription\n",
        " \n",
        "struct SimpleStructure: ExampleProtocol {\n",
        "    var simpleDescription: String = \"A simple structure\"\n",
        "    mutating func adjust() {\n",
        "        simpleDescription += \" (adjusted)\"\n",
        "    }\n",
        "}\n",
        "var b = SimpleStructure()\n",
        "b.adjust()\n",
        "b.simpleDescription"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "OvDuEwsYi-ZN"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Add another requirement to `ExampleProtocol`.\n",
        "// What changes do you need to make to `SimpleClass` and `SimpleStructure` so that they still\n",
        "// conform to the protocol?"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Is_xfbNXdE3t"
      },
      "source": [
        "구조를 수정하는 메서드를 표시하기 위해 `SimpleStructure` 선언에서 `mutating` 키워드를 사용하는 것에 주목하세요. 클래스의 메서드는 항상 클래스를 수정할 수 있으므로 `SimpleClass`의 선언에는 mutating으로 표시된 메서드가 필요하지 않습니다.\n",
        "\n",
        "`extension`을 사용하여 새 메서드 및 계산된 속성과 같은 기존 유형에 기능을 추가합니다. extension을 사용하여 다른 곳에서 선언된 유형 또는 라이브러리 또는 프레임워크에서 가져온 유형에 프로토콜 규칙을 추가할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "w5Hde1HwdE3u"
      },
      "outputs": [],
      "source": [
        "extension Int: ExampleProtocol {\n",
        "    public var simpleDescription: String {\n",
        "        return \"The number \\(self)\"\n",
        "    }\n",
        "    public mutating func adjust() {\n",
        "        self += 42\n",
        "    }\n",
        "}\n",
        "7.simpleDescription"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "fdY5CdWjjCRI"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Write an extension for the `Double` type that adds an `absoluteValue` property."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EoOPuRjZntCx"
      },
      "source": [
        "다른 명명된 유형과 마찬가지로 프로토콜 이름을 사용할 수 있습니다. 예를 들어, 유형이 다르지만 모두 단일 프로토콜 규칙을 따르는 객체 모음을 만들 수 있습니다. 유형이 프로토콜 유형인 값을 처리할 때 프로토콜 정의 밖의 메서드를 사용할 수 없습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "OghTVgiRnvgA"
      },
      "outputs": [],
      "source": [
        "let protocolValue: ExampleProtocol = a\n",
        "protocolValue.simpleDescription"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Xad6Z5Nrn34O"
      },
      "outputs": [],
      "source": [
        "// Uncomment to see the error.\n",
        "// protocolValue.anotherProperty"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "92wBiymPn3HE"
      },
      "source": [
        "변수 `protocolValue`에 `SimpleClass` 런타임 유형이 있더라도 컴파일러는 이를 주어진 `ExampleProtocol` 유형으로 처리합니다. 즉, 프로토콜 규칙 외에 클래스가 구현하는 메서드나 속성에 실수로 액세스할 수 없습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6Z9Y-fWGjxsn"
      },
      "source": [
        "## 오류 처리\n",
        "\n",
        "`Error` 프로토콜을 채택하는 모든 유형을 사용하여 오류를 나타냅니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "PiENY4W2kA2t"
      },
      "outputs": [],
      "source": [
        "enum PrinterError: Error {\n",
        "    case outOfPaper\n",
        "    case noToner\n",
        "    case onFire\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Tu7q13kSkCad"
      },
      "source": [
        "`throw`를 사용하여 오류를 발생시키고 `throws`를 사용하여 오류를 발생시킬 수 있는 함수를 표시합니다. 함수에서 오류를 발생시키면 함수가 즉시 반환하고 함수를 호출한 코드가 오류를 처리합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rY7yWs_nkFbM"
      },
      "outputs": [],
      "source": [
        "func send(job: Int, toPrinter printerName: String) throws -> String {\n",
        "    if printerName == \"Never Has Toner\" {\n",
        "        throw PrinterError.noToner\n",
        "    }\n",
        "    return \"Job sent\"\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uVh7tkr-kPKV"
      },
      "source": [
        "오류를 처리하는 방법에는 여러 가지가 있습니다. 한 가지 방법으로는 `do-catch`를 사용하는 것입니다. `do` 블록 내에서 try를 앞에 작성하여 오류를 발생시킬 수 있는 코드를 작성합니다. `catch` 블록 내에서 다른 이름을 지정하지 않으면 오류에 <code>error</code>라는 이름이 자동으로 제공됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "YejV2PLLkIPA"
      },
      "outputs": [],
      "source": [
        "do {\n",
        "    let printerResponse = try send(job: 1040, toPrinter: \"Bi Sheng\")\n",
        "    print(printerResponse)\n",
        "} catch {\n",
        "    print(error)\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "UJBF1XzdkWjL"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Change the printer name to `\"Never Has Toner\"`, so that the `send(job:toPrinter:)` function\n",
        "// throws an error."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "occBM2Cekddc"
      },
      "source": [
        "특정 오류를 처리하는 여러 `catch` 블록을 제공할 수 있습니다. switch에서 `case` 뒤에 작성하는 것처럼 `catch` 뒤에 패턴을 작성합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "eUhzxcUEkjV-"
      },
      "outputs": [],
      "source": [
        "do {\n",
        "    let printerResponse = try send(job: 1440, toPrinter: \"Gutenberg\")\n",
        "    print(printerResponse)\n",
        "} catch PrinterError.onFire {\n",
        "    print(\"I'll just put this over here, with the rest of the fire.\")\n",
        "} catch let printerError as PrinterError {\n",
        "    print(\"Printer error: \\(printerError).\")\n",
        "} catch {\n",
        "    print(error)\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-qpkCM8NnXie"
      },
      "outputs": [],
      "source": [
        "// Experiment:\n",
        "// Add code to throw an error inside the `do` block.\n",
        "// What kind of error do you need to throw so that the error is handled by the first `catch` block?\n",
        "// What about the second and third blocks?"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JOwTXVWbnAup"
      },
      "source": [
        "오류를 처리하는 또 다른 방법은 `try?`를 사용하여 결과를 선택 사항으로 변환하는 것입니다. 함수에서 오류를 발생시키면 특정 오류는 무시되고 결과는 `nil`입니다. 그렇지 않으면 결과는 함수가 반환한 값을 포함하는 선택 사항입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2ifK6Lyom-9Q"
      },
      "outputs": [],
      "source": [
        "let printerSuccess = try? send(job: 1884, toPrinter: \"Mergenthaler\")\n",
        "let printerFailure = try? send(job: 1885, toPrinter: \"Never Has Toner\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SmSiz49knFZY"
      },
      "source": [
        "`defer`를 사용하여 함수가 반환하기 직전에 함수의 다른 모든 코드 다음에 실행되는 코드 블록을 작성합니다. 함수에서 오류를 발생시키는지 여부와 관계없이 코드가 실행됩니다. `defer`을 사용하여 설정 및 정리 코드를 서로 다른 시간에 실행해야 하는 경우에도 나란히 작성할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tMdhw71anJbP"
      },
      "outputs": [],
      "source": [
        "var fridgeIsOpen = false\n",
        "let fridgeContent = [\"milk\", \"eggs\", \"leftovers\"]\n",
        "\n",
        "func fridgeContains(_ food: String) -> Bool {\n",
        "    fridgeIsOpen = true\n",
        "    defer {\n",
        "        fridgeIsOpen = false\n",
        "    }\n",
        "\n",
        "    let result = fridgeContent.contains(food)\n",
        "    return result\n",
        "}\n",
        "fridgeContains(\"banana\")\n",
        "fridgeIsOpen"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "g0wCWqNMiOv-"
      },
      "source": [
        "## 제네릭\n",
        "\n",
        "제네릭 함수 또는 유형을 만들려면 꺾쇠괄호 안에 이름을 작성합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "sMyq1uQsdE3y"
      },
      "outputs": [],
      "source": [
        "func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {\n",
        "    var result = [Item]()\n",
        "    for _ in 0..<numberOfTimes {\n",
        "        result.append(item)\n",
        "    }\n",
        "    return result\n",
        "}\n",
        "makeArray(repeating: \"knock\", numberOfTimes: 4)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "If8pw1_7dE3z"
      },
      "source": [
        "제네릭 형태의 함수와 메서드는 물론 클래스, 열거형 및 구조를 만들 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "PrhmI6iNdE30"
      },
      "outputs": [],
      "source": [
        "// Reimplement the Swift standard library's optional type\n",
        "enum OptionalValue<Wrapped> {\n",
        "    case none\n",
        "    case some(Wrapped)\n",
        "}\n",
        "var possibleInteger: OptionalValue<Int> = .none\n",
        "possibleInteger = .some(100)\n",
        "print(possibleInteger)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NXhFj_GCdE31"
      },
      "source": [
        "유형 이름 뒤에 `where`를 사용하여 요구 사항의 목록을 지정합니다. 예를 들어, 프로토콜을 구현하는 유형이거나, 두 유형이 같은 유형이거나, 클래스에 특정 슈퍼클래스가 있는 유형이 필요합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EkXn4tmhdE32"
      },
      "outputs": [],
      "source": [
        "func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool\n",
        "    where T.Element: Equatable, T.Element == U.Element\n",
        "{\n",
        "    for lhsItem in lhs {\n",
        "        for rhsItem in rhs {\n",
        "            if lhsItem == rhsItem {\n",
        "                return true\n",
        "            }\n",
        "        }\n",
        "    }\n",
        "    return false\n",
        "}\n",
        "anyCommonElements([1, 2, 3], [3])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6cZJ4EiYq33D"
      },
      "source": [
        "`<T: Equatable>`을 작성하는 것은 `<T> ... where T: Equatable`을 작성하는 것과 같습니다."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "a_swift_tour.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Swift",
      "name": "swift"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
